package main

import (
	"context"
	"errors"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
	"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	"go.opentelemetry.io/otel/log/global"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/sdk/log"
	"go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/trace"
)

// setupOTelSDK 初始化 OpenTelemetry 监控管道
func setupOTelSDK(ctx context.Context) (shutdown func(context.Context) error, err error) {
	// shutdownFunc 存的是各个资源的清理函数
	var shutdownFuncs []func(context.Context) error

	// shutdown 调用在 shutdownFuncs 注册的所有清理函数，所有错误会被合并返回
	shutdown = func(ctx context.Context) error {
		var err error
		for _, fn := range shutdownFuncs {
			// errors.Join 函数用于将多个错误合并成一个复合错误
			err = errors.Join(err, fn(ctx))
		}
		shutdownFuncs = nil
		return err
	}

	// handleErr 把一个错误和 shutdown 函数返回的错误 Join 到一起
	handleErr := func(inErr error) {
		err = errors.Join(inErr, shutdown(ctx))
	}

	// 设置上下文传播器
	prop := newPropagator()
	otel.SetTextMapPropagator(prop)

	// 设置分布式追踪(Trace)提供者
	tracerProvider, err := newTracerProvider()
	if err != nil {
		handleErr(err)
		return
	}
	shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
	otel.SetTracerProvider(tracerProvider)

	// 设置指标(Metric)提供者
	meterProvider, err := newMeterProvider()
	if err != nil {
		handleErr(err)
		return
	}
	shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown)
	otel.SetMeterProvider(meterProvider)

	// 设置日志(Logger)提供者
	loggerProvider, err := newLoggerProvider()
	if err != nil {
		handleErr(err)
		return
	}
	shutdownFuncs = append(shutdownFuncs, loggerProvider.Shutdown)
	global.SetLoggerProvider(loggerProvider)

	return
}

// newPropagator 创建一个集成了 TraceContext 和 Baggage 传播器的复合传播器
// propagation.TextMapPropagator 是一个接口类型，用于跨进程传播上下文
// Inject() 方法将上下文注入到载体（carrier）中
// Extract() 方法从载体中提取上下文并返回一个 context.Context 对象
// Fields() 方法返回一个字符串切片，包含所有注入的键
func newPropagator() propagation.TextMapPropagator {
	// NewCompositeTextMapPropagator 会把实现了 TextMapPropagator 接口的传播器组合成一个切片，
	// 并按顺序调用它们的 Inject 和 Extract 方法
	return propagation.NewCompositeTextMapPropagator(
		propagation.TraceContext{}, // 传播 W3C Trace Context 标准定义的 traceparent 和 traces 头
		propagation.Baggage{},      // 传播自定义的键值对
	)
}

func newTracerProvider() (*trace.TracerProvider, error) {
	traceExporter, err := stdouttrace.New(
		stdouttrace.WithPrettyPrint())
	if err != nil {
		return nil, err
	}

	tracerProvider := trace.NewTracerProvider(
		trace.WithBatcher(traceExporter,
			// 默认5秒批处理间隔，设为1秒便于演示
			trace.WithBatchTimeout(time.Second)),
	)
	return tracerProvider, nil
}

func newMeterProvider() (*metric.MeterProvider, error) {
	metricExporter, err := stdoutmetric.New()
	if err != nil {
		return nil, err
	}

	meterProvider := metric.NewMeterProvider(
		metric.WithReader(metric.NewPeriodicReader(metricExporter,
			// 默认1分钟采集间隔，设为3秒便于演示
			metric.WithInterval(3*time.Second))),
	)
	return meterProvider, nil
}

func newLoggerProvider() (*log.LoggerProvider, error) {
	logExporter, err := stdoutlog.New()
	if err != nil {
		return nil, err
	}

	loggerProvider := log.NewLoggerProvider(
		log.WithProcessor(log.NewBatchProcessor(logExporter)),
	)
	return loggerProvider, nil
}
